package Game;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Iterator;

import graphics.Sprite;
import Game.sprites.Creature;
import util.StopWatch;

/**
    The TileMapRenderer class draws a TileMap on the screen.
    It draws all tiles, sprites, and an optional background image
    centered around the position of the player.

    <p>If the width of background image is smaller the width of
    the tile map, the background image will appear to move
    slowly, creating a parallax background effect.

    <p>Also, three static methods are provided to convert pixels
    to tile positions, and vice-versa.

    <p>This TileMapRender uses a tile size of 64.
*/
public class TileMapRenderer {
    private float mFPS=0;
    private float mMilliSecPerFrame=0;
    //private float fps=0;
    //private final int FRAME_CONSTANT=50;
    //private int frameCount=0;
    //private float timeList[]=new float[FRAME_CONSTANT];
    //private float time=0;
   // private float time_last_frame=0;
    //private StopWatch timer=new StopWatch();
    
    private float numOfFrames=0;
    private float timeElapsed = 0.0f;

    private static final int TILE_SIZE = 64;
    // the size in bits of the tile
    // Math.pow(2, TILE_SIZE_BITS) == TILE_SIZE
    private static final int TILE_SIZE_BITS = 6;

    private Image background;

    /**
        Converts a pixel position to a tile position.
    */
    public static int pixelsToTiles(float pixels) {
        return pixelsToTiles(Math.round(pixels));
    }


    /**
        Converts a pixel position to a tile position.
    */
    public static int pixelsToTiles(int pixels) {
        // use shifting to get correct values for negative pixels
        return pixels >> TILE_SIZE_BITS;

        // or, for tile sizes that aren't a power of two,
        // use the floor function:
        //return (int)Math.floor((float)pixels / TILE_SIZE);
    }


    /**
        Converts a tile position to a pixel position.
    */
    public static int tilesToPixels(int numTiles) {
        // no real reason to use shifting here.
        // it's slighty faster, but doesn't add up to much
        // on modern processors.
        return numTiles << TILE_SIZE_BITS;

        // use this if the tile size isn't a power of 2:
        //return numTiles * TILE_SIZE;
    }


    /**
        Sets the background to draw.
    */
    public void setBackground(Image background) {
        this.background = background;
    }


    /**
        Draws the specified TileMap.
    */
    public  void draw(Graphics2D g, TileMap map,
        int screenWidth, int screenHeight)
    {
        if(map.getBackground()!=background)
            background=map.getBackground();

        //start timer for FPS
        //timer.start();

        Sprite player = map.getPlayer();
        int mapWidth = tilesToPixels(map.getWidth());
        int mapHeight = tilesToPixels(map.getHeight());

        // get the scrolling position of the map
        // based on player's position
        int offsetX = screenWidth / 2 -
            Math.round(player.getX()) - TILE_SIZE;
        //System.out.println("OffsetX is "+offsetX);
        //!!!!!!!!!!!!!!!!!!!!!!
        offsetX = Math.min(offsetX, 0);
        offsetX = Math.max(offsetX, screenWidth - mapWidth);
        //!!!!!!!!!!!!!!!!!!!!!!!!!
        // get the y offset to draw all sprites and tiles
        /*int offsetY = screenHeight -
            tilesToPixels(map.getHeight());*/
        int offsetY = screenHeight/2 - Math.round(player.getY())-TILE_SIZE;
        //offsetY = Math.min(offsetY, 0);
        offsetY = Math.max(offsetY,screenHeight-mapHeight);

        // get the y offset to draw all sprites and tiles
        /*int offsetY = screenHeight -
            tilesToPixels(map.getHeight());
            */
        // draw black background, if needed
        //background=null;//!!!!!!!!!!!!!!!!!!
        if (background == null ||
            screenHeight > background.getHeight(null))
        {
            g.setColor(Color.black);
            g.fillRect(0, 0, screenWidth, screenHeight);
        }

        // draw parallax background image
        if (background != null) {
            
            /*BufferedImage newFrame=new BufferedImage(background.getWidth(null),background.getHeight(null),BufferedImage.TYPE_INT_ARGB);
            Graphics2D tempG= newFrame.createGraphics();
           
            
            tempG.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC,1.0f));
            //g.setComposite(AlphaComposite.Src);
            tempG.drawImage(newFrame, null, 0,0);
            
            background=Toolkit.getDefaultToolkit().createImage(newFrame.getSource());*/
            
            int x = offsetX *
                (screenWidth - background.getWidth(null)) /
                (screenWidth - mapWidth);
            int y = screenHeight - background.getHeight(null);
            
            Composite comp=g.getComposite();
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP,0.66f));
            g.drawImage(background, x, y, null);
            g.setComposite(comp);
        }

        // draw the visible tiles
        int firstTileX = pixelsToTiles(-offsetX);
        //System.out.println("fisrtTileX is "+firstTileX);
        int lastTileX = firstTileX +
            pixelsToTiles(screenWidth) + 1;
        for (int y=0; y<map.getHeight(); y++) {
            for (int x=firstTileX; x <= lastTileX; x++) {
                Tile tile=map.getTile(x, y);
                if (tile != null) {
                    Image image = map.getTile(x, y).getImage();
                    g.drawImage(image,
                        tilesToPixels(x) + offsetX,
                        tilesToPixels(y) + offsetY,
                        null);
                }
            }
        }

        // draw player
        g.drawImage(player.getImage(),
            Math.round(player.getX()) + offsetX,
            Math.round(player.getY()) + offsetY,
            null);

        // draw sprites
        Iterator i = map.getSprites();
        int startX=TileMapRenderer.tilesToPixels(firstTileX-1);
        int finishX=TileMapRenderer.tilesToPixels(lastTileX);
        while (i.hasNext()) {
            Sprite sprite = (Sprite)i.next();
            
            int x = Math.round(sprite.getX()) + offsetX;
            int y = Math.round(sprite.getY()) + offsetY;
            /*if(Math.round(sprite.getX())-((startX/mapWidth)*mapWidth)>=startX && Math.round(sprite.getX())-((startX/mapWidth)*mapWidth)+screenWidth<=finishX){
                System.out.println(sprite.toString()+"is negative"); 
                //x=Math.round(sprite.getX())-((startX/mapWidth)*mapWidth)+offsetX;
                x=Math.round(sprite.getX())-mapWidth+offsetX;
            }
                
            
            System.out.println("Name: "+sprite.toString());  
            System.out.println("offsetX: "+offsetX);
            System.out.println("x: "+x);
            System.out.println("start: "+startX);
            System.out.println("finish: "+finishX+"\n");*/
            if(x>=startX+offsetX && x<=finishX+offsetX)
                //System.out.println("Drawn:"+sprite.toString());
                g.drawImage(sprite.getImage(), x, y, null);
                
            // wake up the creature when it's on screen
            if (sprite instanceof Creature &&
                x >= 0 && x < screenWidth)
            {
                ((Creature)sprite).wakeUp();
            }
        }

        //stop timer for FPS
        /*timer.stop();
        if(frameCount<FRAME_CONSTANT){
            timeList[frameCount]=timer.getElapsedTime();
            frameCount++;
        }
        else{
            frameCount=0;
            float total=0;

            for(int x=0;x<FRAME_CONSTANT;x++){
                total+=timeList[x];
            }
            float avg=total/FRAME_CONSTANT;
            fps=FRAME_CONSTANT/avg;
            //time = time * 0.9f + time_last_frame * 0.1f;
            

        }
        //printFPS(g,fps,screenWidth,screenHeight);
        //calculate FPS
        //time_last_frame=time;*/
    }

    public void printFPS(Graphics2D g,int screenWidth, int screenHeight){
        g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        g.setColor(Color.ORANGE);
        g.drawString("FPS:"+mFPS, 2, 20);
        g.drawString("Millisec per Frame:"+mMilliSecPerFrame, 2, 40);
    }
    
    public void calcFSP(long elapsedTime){
         numOfFrames+=1.0f;
         timeElapsed +=(float)elapsedTime/1000;
         
               if( timeElapsed >= 1.0f )
          {
                // Frames Per Second = numFrames / timeElapsed,
                // but timeElapsed approx. equals 1.0, so
                // frames per second = numFrames.

                mFPS = numOfFrames;

                // Average time, in milliseconds, it took to render a
                // single frame.
                mMilliSecPerFrame = 1000.0f / mFPS;

                // Reset time counter and frame count to prepare
                // for computing the average stats over the next second.
                timeElapsed = 0.0f;
                numOfFrames   = 0.0f;
          }


        
    
    }

}
